<?php
/**
 * This class handles the basic authentication and page requests to Mixi
 */
 class MixiCommon
 {
 	protected $parser;
 	private $authentication;
	
	/**
	 * Constructor of the MixiCommon object.  Sets up initial authentication.
	 * 
	 * @param $auth_object object Object to handle asking for username / password if needed.  This could be a dialog, console input, or even a db pull
	 */
	public function __construct($auth_object, $auth_method)
	{		
		$this->authentication = array($auth_object, $auth_method);
		
		// Attempt to access the home page and parse the return to see if we need to login
		$html = $this->request("http://mixi.jp/home.pl");
	}
	
	/**
	 * Authenticate against Mixi.  Use our auth object to handle authentication.
	 */	
 	protected function auth()
 	{
 		if(!$this->authentication)
 		{ 
 			throw new NoAuthObject("002 - auth_object_empty");
 		}
		else if(count($this->authentication) != '2')
		{
			throw new NoAuthObject("002 - auth_object_corrupt");	
		} 		
 		
 		//Use call_user_func to obtain authentication from an object
 		list($username, $password) = call_user_func($this->authentication);
 		
 		$post_data = array(
 			'email' => $username,
 			'password' => $password,
 			'sticky' => 'on',
 			'next_url' => '/home.pl'
 		);
 		
 		$html = $this->request("https://mixi.jp/login.pl", $post_data);
 		$this->parser->loadHTML($html);
 		$xpath = new DOMXPath($this->parser);
 		$elements = $xpath->query("//*[@http-equiv='refresh']");
 		
 		if($elements->item(0))
 		{
 	 		// Immediately kill sensitive data
	 		$username = '';
 			$password = '';
 			$post_data = array(); 			
 			$this->authenticated = true;
 		}
 		else
 		{
 			// Immediately kill sensitive data
	 		$username = '';
 			$password = '';
 			$post_data = array();
 			throw new LoginFailure("001 - mixi_login_failure");
 		}
 	}
 	
 	/**
 	 * This function checks to see if we're authenticated by looking for the
 	 * login box element on the page.  This seems kind of hacky, but Mixi doesn't
 	 * implement any kind of headers, and relying on cookie contents doesnt' really 
 	 * work for me.  Would be nice if they sent redirection headers instead of just shoving
 	 * the template in :/
 	 * 
 	 * @param $html string The HTML to check against for the login box. Pass by ref of course
 	 * 
 	 * @return bool Whether or not we're authenticated 
 	 */
 	protected function checkAuthentication(&$html)
 	{
 		$this->parser = new DOMDocument();
		$this->parser->loadHTML($html);
		$xpath = new DOMXPath($this->parser);
		$elements = $xpath->query("//*[@id='login_box']");
		
		// Check for the login form element. This is how we know we're authenticated or not
		if($elements->item(0))
		{
			//okay it's there, time to authenticate against mixi
			$this->auth();
			return false; // Let request() know to retry the page
		}
		else
		{
			$this->authenticated = true;
			return true;
		}
 	}
 	
 	/**
 	 * Request a page or post data to a page in Mixi
 	 * Checks against authentication and logs us in if we're not
 	 * 
 	 * @param $url string The URL to retrieve / post to
 	 * @param $data array The post fields, if we're doing a post
 	 * 
 	 * @return string The HTML document 
 	 */
 	protected function request($url, $data=null)
 	{
 		$curl = curl_init($url);
 		// This is used to prevent re-authentication to mixi
 		// We simply load the session cookie they gave us
 		if(file_exists(COOKIE_FILE))
 		{
 			curl_setopt($curl, CURLOPT_COOKIEFILE, COOKIE_FILE);
 		}
 		
 		// Save the login cookie here to use on later startups
 		curl_setopt($curl, CURLOPT_COOKIEJAR, COOKIE_FILE);

 		if($data)
 		{
 			curl_setopt($curl, CURLOPT_POST, true);
 			curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
 		}
 		
 		// We'll need to get the data back for things like authentication
 		curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
 		curl_setopt($curl, CURLOPT_HEADER, 0);
 		
 		// Not setting these causes SSL to go weird.  
 		// TODO: Research how to make this not necessary
 		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,  2);
		
		if(PROXY_ENABLED)
		{
			 curl_setopt($curl, CURLOPT_HTTPPROXYTUNNEL, true);
			 curl_setopt($curl, CURLOPT_PROXY, PROXY_HOST);
			 curl_setopt($curl, CURLOPT_PROXYAUTH, PROXY_AUTH);
			 curl_setopt($curl, CURLOPT_PROXYPORT, PROXY_PORT);
			 curl_setopt($curl, CURLOPT_PROXYTYPE, PROXY_TYPE);
			 curl_setopt($curl, CURLOPT_PROXYUSERPWD, PROXY_USER . ":" . PROXY_PASS);
		}
 		
 		$output = curl_exec($curl);
 		
 		if(!$output)
 		{
 			throw new NoFetchData("003 - request_data_empty");
 		}
 		
 		curl_close($curl);
 		
 		// See if our request was denied because of a failed login
 		if(!$this->checkAuthentication($output))
 		{
 			// Recall the URL again if we weren't authenticated
 			$this->request($url, $data);
 		}
 		
 		return $output;
 	}
 }
?>